
#include "popolazione.hpp"


/*
 *  Costruttore
 */
Popolazione::Popolazione(int p_size,int rows,int cols)
{
	int i;
	if(rows==-1 || cols==-1)
	{
		POP_ROWS = DEF_ROWS;
		POP_COLS = DEF_COLS;

	}
	else
	{
		POP_ROWS = rows;
		POP_COLS = cols;

	}

	//IF_DBG_VERB(printf("[INFO] Popolazione::Popolazione: POP ALLOC: %d,%d\n",POP_ROWS,POP_COLS);)

    this->pop_size=p_size;
	P = (Individuo**)malloc(p_size*sizeof(Individuo*));
	for(i=0; i<p_size; i++)
		this->P[i] = new Individuo(POP_ROWS,POP_COLS);

    Best = NULL;
}

Popolazione::Popolazione(char *path) throw(PopolazioneException)
{
    this->pop_size =0;

	POP_ROWS = DEF_ROWS;
	POP_COLS = DEF_COLS;

    try
    {
        this->load_from_file(path,Popolazione::LOAD_RES_POLICY_RESIZE);
    }
    catch(PopolazioneException &pe)
    {
        std::string errmsg("Popolazione::Popolazione:\n\t");
        errmsg = errmsg + pe.msg;
        throw PopolazioneException((char*)errmsg.c_str(),PopolazioneException::ERR_TYPE_FILEIO);
    }
}

Popolazione::~Popolazione()
{
	unsigned int i;

	for(i=0; i<pop_size; i++)
		delete P[i];

	free(P);
}

void Popolazione::randomInit()
{
    for(unsigned int i =0; i<this->pop_size; i++)
    {
        this->P[i]->randomInit();
        this->P[i]->mod = 1;
    }
}

int Popolazione::save_to_file(int fp,int save_from, int save_to, unsigned int write_mod) throw(PopolazioneException)
{

    int sz,i;
	//clock_t t0,tf;
    //char err[500];

	if(save_to == 0)
		save_to=pop_size-1;

	sz = save_to-save_from+1;

    write(fp,&(sz),sizeof(int));

	write(fp,&POP_ROWS,sizeof(int));
	write(fp,&POP_COLS,sizeof(int));

	//t0 = clock();
    for(i=save_from; i<=save_to; i++)
    {
        try
        {
            this->P[i]->write_genotipo(fp, write_mod);

        }
        catch(IndividuoException &ie)
        {
            std::string errmsg("Popolazione::write_to_file:\n\t");
            errmsg = errmsg + ie.msg;
            throw PopolazioneException((char*)errmsg.c_str(),PopolazioneException::ERR_TYPE_FILEIO);
        }

    }
	//tf = clock();

	//printf("\nSend Time:\t%f\n",(tf-t0)/(float)(CLOCKS_PER_SEC));
	//fflush(stdout);


    return sz;
}

int Popolazione::save_to_file(char *path, unsigned int write_mod) throw(PopolazioneException)
{
    int fp;
    unsigned int sz;
    //char err[500];

    fp=open(path,O_CREAT | O_WRONLY | O_TRUNC, 0700);
    if(fp==-1)
	{
		perror("OPEN: ");
        throw PopolazioneException("Popolazione::save_to_file: impossibile creare il file",PopolazioneException::ERR_TYPE_FILEIO);
	}

	try{
		save_to_file(fp, write_mod);
	}
	catch(PopolazioneException &e)
	{
		throw e;
	}

    close(fp);

    return sz;
}

int Popolazione::load_from_file(int fp, enum pop_load_respolicy rp, unsigned int read_mod) throw(PopolazioneException)
{
    int sz,i,rows,cols;
    char err[500];

    read(fp,&sz,sizeof(int));

	read(fp,&rows,sizeof(int));
	read(fp,&cols,sizeof(int));

	IF_DBG_VERB(printf("[INFO] Popolazione::load_from_file: loading<%d>[%d,%d]\n",sz,rows,cols);)

	if(sz!=this->pop_size || rows!=POP_ROWS || cols!=POP_COLS)
    {
        if(rp == Popolazione::LOAD_RES_POLICY_NORESIZE)
        {
            sprintf(err,"Popolazione::load_from_file: Parametri della popolazione errati. Attesa:%d\tLetta:%d",this->pop_size,sz);
            //close(fp);

            //IF_DBG_INFO(printf("[ERROR] %s\n",err);)
            throw PopolazioneException(err,PopolazioneException::ERR_TYPE_PSIZE);
        }
        else
        {
            IF_DBG_VERB(printf("[INFO] Popolazione::load_from_file: Resize della popolazione (%d[%d,%d]->%d[%d,%d])\n",this->pop_size,POP_ROWS,POP_COLS,sz,rows,cols);)
			if(pop_size!=0)
			{
				for(i=0; i<pop_size; i++)
					delete P[i];
				free(P);
			}
            this->pop_size=sz;

			POP_ROWS = rows;
			POP_COLS = cols;

			P = (Individuo**)malloc(pop_size*sizeof(Individuo*));
			for(int i=0; i<pop_size; i++)
				this->P[i] = new Individuo(POP_ROWS,POP_COLS);

            Best = NULL;
        }
    }

    for(i=0; i<sz; i++)
    {
        try
        {
            this->P[i]->read_genotipo(fp, read_mod);
            //this->P[i]->mod = 1;
        }
        catch(IndividuoException &ie)
        {
            std::string errmsg("Popolazione::load_from_file:\n\t");
            errmsg = errmsg + ie.msg;
            throw PopolazioneException((char*)errmsg.c_str(),PopolazioneException::ERR_TYPE_FILEIO);
        }

    }

    return sz;
}


int Popolazione::load_from_file(char *path, enum pop_load_respolicy rp, unsigned int read_mod) throw(PopolazioneException)
{
    int fp;
    unsigned int sz;
    //char err[500];

    fp=open(path,O_RDONLY);
    if(fp==-1)
        throw PopolazioneException("Popolazione::load_from_file: File not Found",PopolazioneException::ERR_TYPE_FILEIO);

	try{
		load_from_file(fp,rp, read_mod);
	}
	catch(PopolazioneException &e)
	{
		throw e;
	}

    close(fp);

    return sz;
}

float Popolazione::hamming_distance(hamming_type ht)
{
    float partial=0;

    switch(ht)
    {
        case(HAMMING_TYPE_ALLMED):
            //TODO: Implementare HAMMING_TYPE_ALLMED
            printf("[WARN] Non Implementato\n");
            break;
        case(HAMMING_TYPE_BESTMED):
            Individuo b(POP_ROWS,POP_COLS);

            b = this->getBestIndividuo();
            for(unsigned int i=0; i<this->pop_size; i++)
                partial += b.distance(*(this->P[i]));

            partial = partial / (this->pop_size-1);

            break;
    }

    return partial;
}

void Popolazione::operator= (const Popolazione& pop)
{
	int i;
    if(pop.pop_size!=this->pop_size || pop.POP_COLS!=this->POP_COLS || pop.POP_ROWS != this->POP_ROWS)
    {
        IF_DBG_VERB(printf("[WARN] Popolazione::operator= : resize popolazione %d[%d,%d]->%d[%d,%d]\n",this->pop_size,POP_ROWS,POP_COLS,pop.pop_size,pop.POP_ROWS,pop.POP_COLS);)

		if(pop_size!=0)
		{
			for(i=0; i<pop_size; i++)
				delete P[i];
			free(P);
		}
		pop_size = pop.pop_size;

		POP_ROWS = pop.POP_ROWS;
		POP_COLS = pop.POP_COLS;

		P = (Individuo**)malloc(pop_size*sizeof(Individuo*));
		for(int i=0; i<pop_size; i++)
			this->P[i] = new Individuo(POP_ROWS,POP_COLS);

    }

    for(unsigned int i=0; i<pop.pop_size; i++)
        *(this->P[i]) = *(pop.P[i]);
}

Individuo &Popolazione::operator[] (unsigned int index) throw(PopolazioneException)
{
    if(index>=0 && index<this->pop_size)
    {
		return *(this->P[index]);
    }
    else
        throw PopolazioneException("Popolazione::operator[]: out of bound!",PopolazioneException::ERR_TYPE_OOB);
}

void Popolazione::print_all()
{
    for(unsigned int i =0; i<this->pop_size; i++)
    {
        printf("%d:\t%d",i,this->P[i]->fitness);
		if (P[i]->mod == 1)
			printf("#\n");
		else
			printf("\n");
    }
}

const Individuo &Popolazione::getBestIndividuo()
{
    unsigned int maxf,imax=0,i;
    //TODO: Popolazione::getBestIndividuo : Ottimizzare selezione best?
    for(i=0,maxf=0; i<this->pop_size; i++)
    {
        if(this->P[i]->fitness>maxf)
        {
            imax=i; maxf=this->P[i]->fitness;
        }
    }

    return *(this->P[imax]);
}

void Popolazione::selection_tournament_elitism(Popolazione &N,int t_size,float sp)
{

    int i,j,winner,p_size;
    unsigned int fmax;
    //char    str[500];
    int     tp[MAX_TS];
	//Individuo	best(POP_ROWS,POP_COLS,POP_LB);

    if(t_size>MAX_TS) t_size = MAX_TS;

    /*
     *  Step 1: elitism. Ricerco il miglior individuo e lo copio
     */

    //printf("[E\n");
    N[0] = this->getBestIndividuo();

    /*
     *  Step 2: tournament selection
     */
    //printf("G\n");
    for(i=1,p_size=N.pop_size; i<p_size; i++)
    {
        //printf("{C");
        for(j=0; j<t_size; j++)
        {
			tp[j] = rand() % p_size;
        }

        /*
         * Con probabilita' 1-sp scelgo un individuo a caso dal gruppo
         */

        //printf("D");
        if((rand()/(float)RAND_MAX)>sp)
        {

			winner = rand() % t_size;
			winner=tp[winner];
            //printf("C@(%d->%d)",winner,i);
            //printf("*");
        }
        else    //Altrimenti, vince il migliore
        {
            fmax=0;

            for(j=0; j<t_size; j++)
                if(P[tp[j]]->fitness > fmax) {fmax = P[tp[j]]->fitness; winner=tp[j];}

            //printf("C*(%d->%d)[%d]",winner,i,fmax);
        }

        //printf("Winner = %d\n",winner);
        N[i]=*(this->P[winner]);
        //printf("}");
    }
    //printf("]\n");


}


int Popolazione::crossover(Popolazione &N,float cp,HERAuint16 options,crossover_type ct)
{
    unsigned int i,n=0,off=0;

    if(this->pop_size != N.pop_size)
    {
        IF_DBG_INFO(printf("[ERROR] Popolazione::Crossover : dimensioni popolazioni incongruenti\n");)
        IF_DBG_INFO(printf("\t this = %d, N = %d\n",this->pop_size,N.pop_size);)
    }

    if(options & CROSS_OPT_P_ELIT)
    {
        N[0] = *(this->P[0]);
        off=1;
    }

    for(i=0; i<floor((this->pop_size-off)/2.0); i++)
    {

        /*
         *  Con probabilita' pc eseguo un crossover tra i due individui adiacenti
         */
        if( (rand()/(float)RAND_MAX) <= cp)
        {
            Individuo::crossover_single_point(*(this->P[2*i+off]),*(this->P[2*i+1+off]),N[2*i+off],N[2*i+1+off]);
            n++;
        }
        /*
         *  Altrimenti vengono semplicemente copiati
         */
        else
        {
            N[2*i+off]=*(this->P[2*i+off]);
            N[2*i+1+off]=*(this->P[2*i+1+off]);
        }
    }

    if(2*i+off==this->pop_size-1)
        N[2*i+off]=*(this->P[2*i+off]);

    return n;
}

int Popolazione::mutazione_act_adapt(int min_rate,int max_rate,int max_score,float pm,HERAuint16 options)
{
	//TODO: Implementare un vero meccanimo di mutazione adattiva...
    unsigned int mut_bits=0,i=0;

    if(options & MUT_OPT_P_ELIT) i++;

    for(; i<this->pop_size; i++)
        if( (rand()/(float)RAND_MAX) <= pm)
        {
		#ifdef USE_STD_CONFIG
			mut_bits+=P[i]->mutate(5);
		#else
			mut_bits+=P[i]->mutate(5);
		#endif
		}

    return mut_bits;
}

float Popolazione::mean_fitness(float *var)
{
    unsigned int i;
    float med = 0;
    float sum = 0;

    for(i=0; i<this->pop_size; i++)
        med += P[i]->fitness;

    med = med / this->pop_size;

    for(i=0; i<this->pop_size; i++)
        sum +=(P[i]->fitness - med)*(P[i]->fitness - med);

    *var = sqrt(sum / this->pop_size);

    return med;
}

void Popolazione::sort_worse_individual(int n)
{
    int i,j;
    Individuo tmp(POP_ROWS,POP_COLS);

    if(n>pop_size) n = pop_size;

    for(i=0; i<n; i++)
        for(j=0; j<pop_size-i-1; j++)
            if( P[j]->fitness < P[j+1]->fitness)
            {
                tmp = *(P[j]);
                *(P[j]) = *(P[j+1]);
                *(P[j+1]) = tmp;
            }
}

void Popolazione::sort_best_individual(int n)
{
    int i,j;
    Individuo tmp(POP_ROWS,POP_COLS);

    if(n>pop_size) n = pop_size;

    for(i=0; i<n; i++)
        for(j=pop_size-i-1; j>0; j--)
            if( P[j]->fitness < P[j-1]->fitness)
            {
                tmp = *(P[j]);
                *(P[j]) = *(P[j-1]);
                *(P[j-1]) = tmp;
            }
}

int Popolazione::resize(int new_size) throw(PopolazioneException)
{
	int i;

	if(new_size==pop_size)
		return new_size;

	IF_DBG_VERB(printf("[INFO] Popolazione::resize: %d -> %d\n",pop_size,new_size);)

	if(new_size<pop_size)
		for(i=pop_size-1; i>=new_size; i--)
			delete P[i];

	P = (Individuo**)realloc(P,new_size*sizeof(Individuo*));

	if(new_size>pop_size)
		for(i=pop_size; i<new_size; i++)
			this->P[i] = new Individuo(POP_ROWS,POP_COLS);

	this->pop_size=new_size;
    Best = NULL;
	return new_size;
}

Popolazione *Popolazione::copy()
{
	Popolazione *np;

	np = new Popolazione(pop_size, POP_ROWS, POP_COLS);
	(*np)=(*this);
	return np;
}

void Popolazione::load_list(int n, Individuo * individuals[]){
	this->pop_size=n;
	P = (Individuo**)malloc(n*sizeof(Individuo*));
	for(int i=0; i<n; i++)
		{this->P[i] = individuals[i];
		}
	//P=individuals;
    Best = NULL;
}

Individuo * Popolazione::get(int i)
{
 if(i < this->pop_size)
	return P[i];
else
	{
	printf("[ERROR] Popolazione::Request for non existing element\n");
	return NULL;
	}
};

